
use common::{
    base::status::Status,
    serial::api_descriptor::{is_array, is_map, DescriptorProtoHolder, ServiceApiDescriptor},
};
use protobuf::{
    descriptor::{field_descriptor_proto::Type, FieldDescriptorProto},
    CodedInputStream,
};
use pyo3::{
    types::{PyDict, PyList, PyModule},
    IntoPy, Py, PyAny, PyObject, Python,
};

fn new_py_class_instance(msg_name: &str, py_module: &PyModule) -> Result<PyObject, Status> {
    let class_name = &msg_name[1..];
    let py_class = py_module.getattr(class_name);
    if py_class.is_err() {
        return Err(Status::error(format!("{:?}", py_class.err().unwrap())));
    }
    let instance = py_class.unwrap().call0();
    if instance.is_err() {
        return Err(Status::error(format!("{:?}", instance.err().unwrap())));
    }
    let instance = instance.unwrap();
    Ok(instance.into())
}

fn get_field(tag: u32, desc_proto_holder: &DescriptorProtoHolder) -> Option<&FieldDescriptorProto> {
    let field_index = desc_proto_holder.field_index_map.get(&tag);

    if field_index.is_none() {
        return None;
    }

    let field_index = field_index.unwrap();

    desc_proto_holder
        .descriptor_proto
        .field
        .get(*field_index as usize)
}

fn put_value_in_py_obj(
    field: &FieldDescriptorProto,
    value: Py<PyAny>,
    instance: &Py<PyAny>,
    py: &Python,
) {
    let field_name = field.name.as_ref().unwrap().as_str();
    if is_array(field) {
        let ele_list = instance.getattr(py.clone(), field_name);

        if ele_list.is_err() {
            let new_list = PyList::empty(py.clone());
            new_list.append(value).unwrap();
            instance.setattr(py.clone(), field_name, new_list).unwrap();
        } else {
            let ele_list = ele_list.as_ref().unwrap();
            let ele_list = ele_list.downcast::<PyList>(py.clone()).unwrap();
            ele_list.append(value).unwrap();
        };
    } else {
        instance.setattr(py.clone(), field_name, value).unwrap();
    }
}

fn read_proto_to_py_value(
    field_type: Type,
    field: &FieldDescriptorProto,
    msg_name: &str,
    service_api_descriptor: &ServiceApiDescriptor,
    py: &Python,
    py_module: &PyModule,
    input_stream: &mut CodedInputStream,
) -> Result<Py<PyAny>, Status> {
    match field_type {
        // i64
        Type::TYPE_INT64 => {
            let value = input_stream.read_int64();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 i64 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();
            Ok(value.into_py(py.clone()))
        }
        // ui64
        Type::TYPE_UINT64 => {
            let value = input_stream.read_uint64();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 ui64 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();
            Ok(value.into_py(py.clone()))
        }
        // i32
        Type::TYPE_INT32 => {
            let value = input_stream.read_int32();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 i32 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();

            Ok(value.into_py(py.clone()))
        }
        // fixed64
        Type::TYPE_FIXED64 => {
            let value = input_stream.read_fixed64();
            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 fixed64 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }
            let value = value.unwrap();

            Ok(value.into_py(py.clone()))
        }
        // fixed32
        Type::TYPE_FIXED32 => {
            let value = input_stream.read_fixed32();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 fixed32 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();

            Ok(value.into_py(py.clone()))
        }
        // u32
        Type::TYPE_UINT32 => {
            let value = input_stream.read_uint32();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 u32 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();

            Ok(value.into_py(py.clone()))
        }
        // enum
        Type::TYPE_ENUM => {
            let value = input_stream.read_int32();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 enum 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();

            Ok(value.into_py(py.clone()))
        }
        // sfixed32
        Type::TYPE_SFIXED32 => {
            let value = input_stream.read_sfixed32();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 sfixed32 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();

            Ok(value.into_py(py.clone()))
        }
        // sfixed64
        Type::TYPE_SFIXED64 => {
            let value = input_stream.read_sfixed64();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 sfixed64 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();

            Ok(value.into_py(py.clone()))
        }
        // sint32
        Type::TYPE_SINT32 => {
            let value = input_stream.read_sint32();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 sint32 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();

            Ok(value.into_py(py.clone()))
        }
        // sint64
        Type::TYPE_SINT64 => {
            let value = input_stream.read_sint64();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 sint64 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }
            let value = value.unwrap();
            Ok(value.into_py(py.clone()))
        }
        // double
        Type::TYPE_DOUBLE => {
            let value = input_stream.read_double();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 double 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();
            Ok(value.into_py(py.clone()))
        }
        //
        Type::TYPE_FLOAT => {
            let value = input_stream.read_float();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 float 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();

            Ok(value.into_py(py.clone()))
        }
        // string
        Type::TYPE_STRING => {
            let value = input_stream.read_string();

            if value.is_err() {
                return Err(Status::error(format!(
                    "读取 {} {} 为 string 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();

            Ok(value.into_py(py.clone()))
        }
        // 对象
        Type::TYPE_MESSAGE => {
            let type_name = field.type_name.as_ref().unwrap();
            let len = input_stream.read_raw_varint64();
            if len.is_err() {
                return Err(Status::error(format!(
                    "读取  {} {} 为 message 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let len = len.unwrap();

            let old_limit = input_stream.push_limit(len);

            if old_limit.is_err() {
                return Err(Status::error(format!(
                    "读取  {} {} 为 message 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }
            let old_limit = old_limit.unwrap();

            let value = proto_to_pyobj_by_is(
                type_name,
                service_api_descriptor,
                py_module,
                input_stream,
                py,
            )?;

            input_stream.pop_limit(old_limit);

            Ok(value)
        }
        // 对象
        Type::TYPE_BOOL => {
            let value = input_stream.read_bool();
            if value.is_err() {
                return Err(Status::error(format!(
                    "读取  {} {} 为 bool 错误！",
                    msg_name,
                    field.name.as_ref().unwrap()
                )));
            }

            let value = value.unwrap();

            Ok(value.into_py(py.clone()))
        }
        //
        _ => {
            return Err(Status::error(format!(
                "无法识别 {} {} 的类型",
                msg_name,
                field.name.as_ref().unwrap()
            )));
        }
    }
}

//
pub fn proto_to_pyobj_by_is(
    msg_name: &str,
    service_api_descriptor: &ServiceApiDescriptor,
    py_module: &PyModule,
    input_stream: &mut CodedInputStream,
    py: &Python,
) -> Result<PyObject, Status> {
    let msg_descriptor_holder = service_api_descriptor
        .message_descriptor_holder
        .get(msg_name);

    if msg_descriptor_holder.is_none() {
        return Err(Status::error(format!("类型 {} 不存在", msg_name)));
    }

    let msg_descriptor_holder = msg_descriptor_holder.unwrap();

    let instance = new_py_class_instance(msg_name, py_module)?;

    while let Some(tag) = input_stream.read_raw_tag_or_eof().unwrap() {
        let field = get_field(tag, msg_descriptor_holder);
        if field.is_none() {
            continue;
        }
        let field = field.unwrap();
        let field_type = field.type_.unwrap().unwrap();
        if field_type == Type::TYPE_MESSAGE && is_map(field, service_api_descriptor) {
            // 是 map 类型
            let len = input_stream.read_raw_varint32().unwrap();
            let old_limit = input_stream.push_limit(len as u64).unwrap();
            //
            let map_type_name = field.type_name.as_ref().unwrap();
            let map_msg_descriptor_holder = service_api_descriptor
                .message_descriptor_holder
                .get(map_type_name);

            if map_msg_descriptor_holder.is_none() {
                return Err(Status::error(format!("map {} 描述不存在", map_type_name)));
            }
            //
            let map_msg_descriptor_holder = map_msg_descriptor_holder.unwrap();
            //
            let py_dict = PyDict::new(py.clone());
            while let Some(tag) = input_stream.read_raw_tag_or_eof().unwrap() {
                let field = get_field(tag, map_msg_descriptor_holder);
                if field.is_none() {
                    continue;
                }
                let field = field.unwrap();
                let field_type = field.type_.unwrap().unwrap();
                //
                let mut key: Py<PyAny> = py.None();
                let mut value: Py<PyAny> = py.None();

                if field.number() == 1 {
                    key = read_proto_to_py_value(
                        field_type,
                        field,
                        msg_name,
                        service_api_descriptor,
                        py,
                        py_module,
                        input_stream,
                    )?;
                } else {
                    value = read_proto_to_py_value(
                        field_type,
                        field,
                        msg_name,
                        service_api_descriptor,
                        py,
                        py_module,
                        input_stream,
                    )?;
                }
                py_dict.set_item(key, value).unwrap();
                input_stream.pop_limit(old_limit);
            }

            put_value_in_py_obj(field, py_dict.into(), &instance, py);
        } else {
            let value = read_proto_to_py_value(
                field_type,
                field,
                msg_name,
                service_api_descriptor,
                py,
                py_module,
                input_stream,
            )?;
            put_value_in_py_obj(field, value, &instance, py);
        }
    }

    Ok(instance)
}

pub fn proto_to_pyobj(
    proto_bytes: &Vec<u8>,
    msg_name: &str,
    service_api_descriptor: &ServiceApiDescriptor,
    py_module: &PyModule,
    py: &Python,
) -> Result<PyObject, Status> {
    //
    let mut input_stream = CodedInputStream::from_bytes(&*proto_bytes);

    proto_to_pyobj_by_is(
        msg_name,
        service_api_descriptor,
        py_module,
        &mut input_stream,
        py,
    )
}
